
#include "precompiled.h"


#pragma comment(lib, "d3d9.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")
#pragma comment (lib, "d3dx9.lib")

#include "MenuScreenView.h"
#include "MainMenuController.h"
#include "GameController.h"
#include "SpaceShipFactory.h"
#include "GameView.h"
#include "AsteroidFactory.h"
#include "DXFactory.h"



#include "NetworkMenuController.h"

#include "ServerListenController.h"
#include "ServerController.h"

#include "ClientSeekController.h"
#include "ClientController.h"

#include "GameOverController.h"

#include "NetworkPat.h"

// turns on debug messages for mobiles
//#define MOBILE_DEBUG
// turns on debug messages for collisions
//#define COLLISION_DEBUG

//#define _CRTDBG_MAP_ALLOC

using namespace std;

//this is a crappy global but it's for the wndproc so there's really not a great way around it
MessageHandler* G_globalMessageHandler = NULL;
CRITICAL_SECTION wndprocsync;


DWORD WINAPI StartGameLoop(void* param);

//entrypoint
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){

#ifdef _CRTDBG_MAP_ALLOC
    int tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
	tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
    _CrtSetDbgFlag(tmpFlag);
#endif







	//windows overhead- create our window and stuff
	WindowFactory windowFactory(1024,768, MainWindProc);	
	RECT size;
	size.left = 0;
	size.top = 0;
	size.bottom = 768;
	size.right = 1024;

	//get a new window from the factory
	HWND window = windowFactory.GetWindow(hInstance);
	
	InitializeCriticalSection(&wndprocsync);

	//initialize the directx stuff
	DXFactory* dxFactory = new DXFactory(hInstance, window);
	dxFactory->Init();

	//display our window.  It should be doing whatever directx told it to
	ShowWindow(window, nShowCmd);

	//create the screen object for drawing
	Screen* screen = new Screen(window, size, dxFactory->GetGraphicsDevice());

  //create the network engine
  NetworkPat* network = new NetworkPat();
	//create the models
	MenuModel* menuModel = new MenuModel();
	GameModel* gameModel = new GameModel();

	NetworkMenuModel* netMenuModel = new NetworkMenuModel();
  ServerListenModel* sListenModel = new ServerListenModel();
  ServerModel* sModel = new ServerModel();
  ClientSeekModel* cSeekModel = new ClientSeekModel();
  ClientModel* cModel = new ClientModel();
  GameOverModel* gModel = new GameOverModel();
  
	HRESULT res;
	
	LPDIRECT3DTEXTURE9 backgroundImage;
	res = D3DXCreateTextureFromFile(dxFactory->GetGraphicsDevice(), backgroundfilename, &backgroundImage);
	if(res != D3D_OK)
		throw new exception;


	//init views
	View* menuView = new MenuScreenView(screen, menuModel, dxFactory->GetGraphicsDevice());
	View* gameView = new GameView(screen, gameModel, dxFactory->GetGraphicsDevice());
	View* netMenuView = new NetworkMenuView(screen, netMenuModel, dxFactory->GetGraphicsDevice());
  View* sListenView = new ServerListenView(screen, sListenModel, dxFactory->GetGraphicsDevice());
  View* sView = new ServerView(screen, sModel, dxFactory->GetGraphicsDevice());
  View* cSeekView = new ClientSeekView(screen, cSeekModel, dxFactory->GetGraphicsDevice());
  View* cView = new ClientView(screen, cModel, dxFactory->GetGraphicsDevice());
  View* gView = new GameOverView(screen, gModel, dxFactory->GetGraphicsDevice());

	gameView->SetSprite(dxFactory->GetSprite());
	menuView->SetSprite(dxFactory->GetSprite());
	netMenuView->SetSprite(dxFactory->GetSprite());
  sListenView->SetSprite(dxFactory->GetSprite());
  sView->SetSprite(dxFactory->GetSprite());
  cSeekView->SetSprite(dxFactory->GetSprite());
  cView->SetSprite(dxFactory->GetSprite());
  gView->SetSprite(dxFactory->GetSprite());

	gameView->SetBackground(backgroundImage);
	menuView->SetBackground(backgroundImage);
	netMenuView->SetBackground(backgroundImage);
  sListenView->SetBackground(backgroundImage);
  sView->SetBackground(backgroundImage);
  cSeekView->SetBackground(backgroundImage);
  cView->SetBackground(backgroundImage);
  gView->SetBackground(backgroundImage);

	LPD3DXFONT gameScoreText;

	D3DXCreateFont(
		dxFactory->GetGraphicsDevice(), 14, 0, FW_NORMAL, 1, 
		false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 
		DEFAULT_QUALITY, DEFAULT_PITCH || FF_DONTCARE, 
		"Arial", &gameScoreText);

	//init textures
	LPDIRECT3DTEXTURE9 temptext = NULL;
	res = 
		D3DXCreateTextureFromFileEx(dxFactory->GetGraphicsDevice(), "ships.jpg", D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8, 
		D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(0,0,0), NULL, NULL, &temptext);

	if(FAILED(res))
		return 0;

	LPDIRECT3DTEXTURE9 asteroidText;

	res = D3DXCreateTextureFromFileEx(dxFactory->GetGraphicsDevice(), "space3.jpg", D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8, 
		D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(0,0,0), NULL, NULL, &asteroidText);

	if(FAILED(res))
		return 0;


	//rects for spaceships and such
	RECT spaceShipTextureRect;
	spaceShipTextureRect.top = 0;
	spaceShipTextureRect.left = 0;
	spaceShipTextureRect.bottom = 50;
	spaceShipTextureRect.right = 80;

	RECT asteroidTextureRect;
	asteroidTextureRect.top = 28;
	asteroidTextureRect.left = 380;
	asteroidTextureRect.bottom = 80;
	asteroidTextureRect.right = 440;


	//init factories	
	AsteroidFactory* afact = new AsteroidFactory(asteroidText, asteroidTextureRect);
	SpaceShipFactory* sfact = new SpaceShipFactory(temptext, spaceShipTextureRect);
	afact->SetFont(gameScoreText);
	

	//init controllers (these need views and factories sometimes so we should have them created already
	Controller* menuController = new MainMenuController(menuModel, menuView);
	Controller* gameController = new GameController(gameModel, gameView, afact, sfact);
  Controller* netMenuController = new NetworkMenuController(netMenuModel, netMenuView, network);
  Controller* sListenController = new ServerListenController(sListenModel, sListenView, network);
  Controller* sController = new ServerController(sModel, sView, network);
  Controller* cSeekController = new ClientSeekController(cSeekModel, cSeekView, network);
  Controller* cController = new ClientController(cModel, cView, network, afact, sfact);
  Controller* gController = new GameOverController(gModel, gView, network);

	//init the main game object and add controllers and message handlers
	Game* game = new Game(screen, dxFactory);
	game->AddController(menuController, GameState::MainMenu);
	game->AddController(gameController, GameState::Playing);
	game->AddController(netMenuController, GameState::NetworkMenu);
  game->AddController(sListenController, GameState::ServerListen);
  game->AddController(sController, GameState::ServerPlay);
  game->AddController(cSeekController, GameState::ClientSeek);
  game->AddController(cController, GameState::ClientPlay);
  game->AddController(gController, GameState::GameOver);

	game->RegisterMessageHandler(GameState::MainMenu, G_globalMessageHandler);
	game->RegisterMessageHandler(GameState::NetworkMenu, G_globalMessageHandler);
  game->RegisterMessageHandler(GameState::ServerListen, G_globalMessageHandler);
  game->RegisterMessageHandler(GameState::ServerPlay, G_globalMessageHandler);
  game->RegisterMessageHandler(GameState::ClientSeek, G_globalMessageHandler);
  game->RegisterMessageHandler(GameState::GameOver, G_globalMessageHandler);

	MessageHandler* DImessageHandler = new DInputKeyboardhandler(dxFactory->GetKeyboard());
	game->RegisterMessageHandler(GameState::Playing, DImessageHandler);
  game->RegisterMessageHandler(GameState::ClientPlay, DImessageHandler);


	// don't set this to 1 because the player's sprite always has ID 1
	IDFactory::ResetID(9);
//	_CrtSetBreakAlloc(25857);

	// start the game loop
	HANDLE messageThread = CreateThread(NULL, 0, StartGameLoop, game, 0, NULL);


	MSG msg;
	while(true){
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
			if(msg.message == WM_QUIT)
				break;

		TranslateMessage( &msg );

		DispatchMessage( &msg );
		}
		if(WaitForSingleObject(messageThread, 5) == WAIT_OBJECT_0)
			PostQuitMessage(0);
   
   }

	backgroundImage->Release();
	temptext->Release();
	asteroidText->Release();
	gameScoreText->Release();

	delete DImessageHandler;
	delete G_globalMessageHandler;
	delete menuModel;
	delete menuView;
	delete menuController;

	delete gameController;
	delete gameModel;
	delete gameView;

	delete netMenuController;
	delete netMenuModel;
	delete netMenuView;

	delete screen;
	delete game;
	delete dxFactory;
	delete afact;
	delete sfact;
	DeleteCriticalSection(&wndprocsync);
//	_CrtDumpMemoryLeaks();

	return 0;
}


//this is the actual game loop.  InitGameLoop() is blocking and returns if the game
//is over (i.e. the user quits).  Send the game a quit message to force it to quit
//at any time
DWORD WINAPI StartGameLoop(void* param){

	Game* game = (Game*)param;
	
	game->InitGameLoop();
	PostQuitMessage(0);

	
	return 0;



}